import os
from typing import Optional, List

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
from pydantic.v1 import BaseModel, Field

api_key = os.getenv("DASHSCOPE_API_KEY")
# 检查API密钥是否存在，若不存在则抛出错误提示
if not api_key:
    raise ValueError("请设置环境变量DASHSCOPE_API_KEY（阿里云百炼API-KEY）")
# 1. 创建大语言模型实例（适配阿里云百炼的DeepSeek模型）
model = ChatOpenAI(
    model="qwen-plus-latest",  # 模型名称，需与阿里云百炼平台支持的模型名一致
    temperature=0,  # 生成内容的随机性（0表示确定性输出，1表示最大随机性）
    max_tokens=None,  # 最大生成 tokens 数（None表示使用模型默认值）
    timeout=None,  # 超时时间（None表示无超时限制）
    max_retries=2,  # 调用失败时的最大重试次数
    api_key=SecretStr(api_key),  # 以安全字符串形式传递API密钥
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 阿里云百炼的OpenAI兼容接口地址
)


# pydantic:处理数据,验让数据,数据的格式,虚拟化和反虚拟化,类型转换等等定义.
# 定义一个数据
class Person(BaseModel):
    """
    关于一个人的数据类型
    """
    name: Optional[str] = Field(default=None, description="表示人的名族")

    hair_color: Optional[str] = Field(default=None, description="头发颜色")
    height_in_meters: Optional[str] = Field(default=None, description="以米为单位测量的高度")


class ManyPersons(BaseModel):
    """
    多个人
    """
    people: List[Person]


# 定义自定义提示以提供指令和任何其他上下文。
# 1)你可以在提示模板中添加不例以提高提取质量
# 2)引入额外的参数以考虑上下文(例如，包括有关提议文本的文档的元数据
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是一个专业的提取算法。只从未结构化文木中提取相关信息。"
            "如果你不知道要提取的属性的值, 返回该属性的值为null。",
        ),
        ("human", "{text}")
    ]
)

chain = {'text': RunnablePassthrough()} | prompt | model.with_structured_output(schema=ManyPersons)
# text = '马路上走来一个女生，长长的黑头发披在肩上，大概一米七左右。'
text = '马路上走来一个女生，长长的黑头发披在肩上，大概一米七左右。走在她旁边的是她男朋友,叫:刘海,比她还高10厘米'
resp = chain.invoke(text)
print(resp)
